---
title: Using Storybook
description: Easily use Panda with Storybook with our dedicated integration.
---

Learn how to set up Panda CSS in a Storybook project using PostCSS.

## Setup

We are assuming that you already have a project set up with a framework like React, Vue or Svelte.

<Steps>

### Install Storybook

Storybook needs to be installed into a project that is already set up with a framework. It will not work on an empty
project.

<Tabs items={['pnpm', 'npm', 'yarn', 'bun']}>
{/* <!-- prettier-ignore-start --> */}
  <Tab>
    ```bash
    pnpm dlx storybook@latest init
    ```
  </Tab>
  <Tab>
    ```bash
    npx storybook@latest init
    ```
  </Tab>
  <Tab>
    ```bash
    yarn dlx storybook@latest init
    ```
  </Tab>
  <Tab>
    ```bash
    bunx storybook@latest init
    ```
  </Tab>
{/* <!-- prettier-ignore-end --> */}
</Tabs>

### Install Panda

Install panda and create your `panda.config.ts` file.

<Tabs items={['pnpm', 'npm', 'yarn', 'bun']}>
  {/* <!-- prettier-ignore-start --> */}
  <Tab>
    ```bash
    pnpm install -D @pandacss/dev
    pnpm panda init --postcss
    ```
  </Tab>
  <Tab>
    ```bash
    npm install -D @pandacss/dev
    npx panda init --postcss
    ```
  </Tab>
  <Tab>
    ```bash
    yarn add -D @pandacss/dev
    yarn panda init --postcss
    ```
  </Tab>
  <Tab>
    ```bash
    bun add -D @pandacss/dev
    bun panda init --postcss
    ```
  </Tab>
  {/* <!-- prettier-ignore-end --> */}
</Tabs>

If you are using Storybook with the Vite builder, you will have to update your PostCSS config file to use the array
syntax for the plugins instead of the object syntax. Simply change `postcss.config.[c]js`:

```diff filename="postcss.config.js"
module.exports = {
-  plugins: {
-   '@pandacss/dev/postcss': {}
-  }
+  plugins: [require('@pandacss/dev/postcss')()]
}
```

### Update package.json scripts

Open your `package.json` file and update the `scripts` section as follows:

```diff {3} filename="web/package.json"
{
  "scripts": {
+    "prepare": "panda codegen"
  }
}
```

- `"prepare"` - script that will run Panda CSS CLI codegen before each build. Read more about
  [codegen](/docs/references/cli#panda-codegen) in the CLI section.

> This step ensures that the panda output directory is regenerated after each dependency installation. So you can add
> the output directory to your `.gitignore` file and not worry about it.

### Configure the content

Make sure that all of the paths of your Storybook components are included in the `include` section of the
`panda.config.ts` file.

```ts {7} filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'

export default defineConfig({
  // Whether to use css reset
  preflight: true,
  // Where to look for your css declarations
  include: ['./src/**/*.{js,jsx,ts,tsx}', './pages/**/*.{js,jsx,ts,tsx}', './stories/**/*.{js,jsx,ts,tsx}'],
  // Files to exclude
  exclude: [],
  // The output directory for your css system
  outdir: 'styled-system'
})
```

### Configure the entry CSS with layers

Locate your main CSS file and add the following layers:

```css filename="src/index.css"
@layer reset, base, tokens, recipes, utilities;
```

### Import the CSS in your Storybook config

Locate your `.storybook/preview.ts` file and import the CSS file.

In this example CSS file is located in the `src` folder.

```ts {1} filename=".storybook/preview.ts"
import '../src/index.css'

import type { Preview } from '@storybook/react'

const preview: Preview = {
  parameters: {
    actions: { argTypesRegex: '^on[A-Z].*' },
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/
      }
    }
  }
}

export default preview
```

### Start the Storybook server

Run the following command to start your Storybook server.

<Tabs items={['pnpm', 'npm', 'yarn', 'bun']}>
  {/* <!-- prettier-ignore-start --> */}
  <Tab>
    ```bash
    pnpm storybook
    ```
  </Tab>
  <Tab>
    ```bash
    npm run storybook
    ```
  </Tab>
  <Tab>
    ```bash
    yarn storybook
    ```
  </Tab>
  <Tab>
    ```bash
    bun storybook
    ```
  </Tab>
  {/* <!-- prettier-ignore-end --> */}
</Tabs>

### Start using Panda

Now you can start using Panda CSS in Storybook.

Here is the example of a Button component and its corresponding Storybook story:

```tsx filename="src/stories/Button.tsx"
import { ReactNode } from 'react'
import { css } from '../../styled-system/css'

export interface IButtonProps {
  children: ReactNode
}

export const Button = ({ children }: IButtonProps) => {
  return (
    <button
      className={css({
        bg: 'red.300',
        fontFamily: 'Inter',
        px: '4',
        py: '3',
        borderRadius: 'md',
        _hover: { bg: 'red.400' }
      })}
    >
      {children}
    </button>
  )
}
```

```tsx filename="src/stories/Button.stories.tsx"
import type { Meta, StoryObj } from '@storybook/react'
import { css } from '../../styled-system/css'

import { Button } from './Button'

const meta = {
  title: 'Example/Button',
  component: Button,
  tags: ['autodocs'],
  decorators: [
    Story => (
      <div className={css({ m: 10 })}>
        <Story />
      </div>
    )
  ]
} satisfies Meta<typeof Button>

export default meta
type Story = StoryObj<typeof meta>

export const Default: Story = {
  args: {
    children: 'Hello 🐼!'
  }
}
```

</Steps>

## Configuring Dark Mode

To enable dark mode in Storybook, you can use the `@storybook/addon-themes` package.

```bash
pnpm add -D @storybook/addon-themes
```

Then, update your `.storybook/preview.ts` file to include the following:

```ts filename=".storybook/preview.ts"
import { withThemeByClassName } from '@storybook/addon-themes'
import type { Preview, ReactRenderer } from '@storybook/react'

const preview: Preview = {
  // ...
  decorators: [
    withThemeByClassName<ReactRenderer>({
      themes: {
        light: '',
        dark: 'dark'
      },
      defaultTheme: 'light'
    })
  ]
}

export default preview
```

With that in place, you should see the light/dark switcher in Storybook's toolbar.

## Troubleshooting

### Cannot find postcss plugin

If you are having issues with the PostCSS plugin similar to `Cannot find module '@pandacss/dev/postcss'`, update the
PostCSS config as follows:

```js filename="postcss.config.js"
module.exports = {
  plugins: [require('@pandacss/dev/postcss')]
}
```

### HMR not triggered

If you are having issues with HMR not being triggered after a `panda.config.ts` change (or one of its
[dependencies](/docs/references/config#dependencies), you can manually specify the files that should trigger a rebuild
by adding the following to your `panda.config.ts`:

```js filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'

export default defineConfig({
  // ...
  dependencies: ['path/to/files/**.ts']
})
```

### Styles in `args` is not generated

If you are having issues with your `args` not generating the expected CSS, it's probably because of:

- you didn't add a file glob for the Storybook files in your [`config.include`](/docs/references/config#include) like
  `"./stories/**/*.{js,jsx,ts,tsx}"`
- you didn't wrap your `args` object (or some part of it) with the
  [`.raw()` marker that helps Panda find style usage](https://panda-css.com/docs/guides/dynamic-styling#alternative)

```tsx filename="stories/Button.stories.tsx"
import type { Meta, StoryObj } from '@storybook/react'
import { button } from '../../styled-system/recipes'

export const Funky: Story = {
  // mark this as a button recipe usage
  args: button.raw({
    visual: 'funky',
    shape: 'circle',
    size: 'sm'
  })
}
```

### Some recipes styles are missing

If you are having issues with your config `recipes` or `slotRecipes` not generating the expected CSS, it's probably
because of:

- you didn't add a file glob for the Storybook files in your [`config.include`](/docs/references/config#include) like
  `"./stories/**/*.{js,jsx,ts,tsx}"`
- you haven't used every recipes variants in your app, so you might want to pre-generate it (only for storybook usage)
  with [`staticCss`](/docs/guides/static)

```tsx filename="panda.config.ts"
import { defineConfig } from '@pandacss/dev'

export default defineConfig({
  // ...
  staticCss: {
    recipes: '*'
  }
})
```
